package com.medistat.persistance.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.medistat.model.Department;
import com.medistat.model.Doctor;
import com.medistat.model.FreeDay;
import com.medistat.model.Holiday;
import com.medistat.model.Person;
import com.medistat.model.Setting;
import com.medistat.model.StandardPersistentObject;
import com.medistat.model.User;
import com.medistat.model.WeekendDutiesInfo;
import com.medistat.model.Work;
import com.medistat.model.enums.Days;
import com.medistat.model.enums.TypeOfWork;
import com.medistat.model.enums.WeekendDutiesInfoCreationPurpose;
import com.medistat.model.utils.ModelUtils;
import com.medistat.persistance.service.interfaces.Comparator;
import com.medistat.persistance.service.interfaces.DaoService;
import com.medistat.persistance.service.interfaces.Parameter;
import com.medistat.persistance.service.interfaces.PersistanceService;
import com.medistat.util.DateUtils;
import com.medistat.util.ObjectFactory;

@Service("persistanceService")
public class PersistanceServiceImpl implements PersistanceService {

	private DaoService daoService;

	@Autowired
	public void setDaoService(DaoService daoService) {
		this.daoService = daoService;
	}
	
	@Override
	public <T extends StandardPersistentObject> void deleteObject(T object) throws Exception {
		daoService.deleteObject(object);
	}

	@Override
	public void addSetting(Setting setting, Long departmentId) throws Exception {
		Department department = getDepartment(departmentId);
		ModelUtils.addDependentToParent(setting, department, Department.class);
		daoService.updateObject(department);
		//addDependentToParent(setting, departmentId, Department.class);
	}

	public Setting getSetting(Long settingId) throws Exception {
		Setting setting = getObject(settingId, Setting.class);
		return setting;
	}

	@Override
	public void deleteSetting(Setting setting) throws Exception {
		daoService.deleteObject(setting);
	}

	@Override
	public void addUser(User user, Long departmentId) throws Exception {
		Department department = getDepartment(departmentId);
		ModelUtils.addDependentToParent(user, department, Department.class);
		daoService.updateObject(department);
		//addDependentToParent(user, departmentId, Department.class);
	}

	@Override
	public User getUser(String userName, String password) throws Exception {
		List<User> users = null;
		try {

			Parameter parameterUsername = new Parameter("username", userName);
			Parameter parameterPassword = new Parameter("password", password);
			users = daoService.findObjectsByCriteria(User.class, parameterUsername, parameterPassword);
			if (users == null || users.isEmpty()) {
				return null;
			}
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
		return users.get(0);
	}

	@Override
	public void updateUser(User user) throws Exception {
		if (user == null) {
			return;
		}
		daoService.updateObject(user);
	}

	@Override
	public void deleteUser(User user) throws Exception {
		daoService.deleteObject(user);
	}

	@Override
	public void addDepartment(Department department) throws Exception {
		try {
			if (department == null) {
				return;
			}
			daoService.saveObject(department);
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	@Override
	public void addDoctor(Doctor doctor, Long departmentId) throws Exception {
		Department department = getDepartment(departmentId);
		ModelUtils.addDependentToParent(doctor, department, Department.class);
		daoService.updateObject(department);
		//addDependentToParent(doctor, departmentId, Department.class);
	}
	
	@Override
	public void updateDoctor(Doctor doctor) throws Exception {
		if (doctor == null) {
			return;
		}
		daoService.updateObject(doctor);
	}

	@Override
	public void deleteDoctor(Doctor doctor) throws Exception {
		daoService.deleteObject(doctor);
	}
	
	@Override
	public Doctor findDoctor(String name, String surname) throws Exception {
		Parameter parameterName = new Parameter("name", name);
		Parameter parameterSurname = new Parameter("surname", surname);
		Doctor doctor = daoService.findObjectByCriteria(Doctor.class, parameterName, parameterSurname);
		return doctor;
	}

	@Override
	public List<Doctor> getActiveDoctors() {
		Parameter parameterActive = new Parameter("active", Boolean.TRUE);
		List<Doctor> doctors = daoService.findObjectsByCriteria(Doctor.class, parameterActive);
		if (doctors == null || doctors.isEmpty()) {
			return null;
		}
		return doctors;
	}

	public Department getDepartment(Long departmentUid) throws Exception {
		Department department = getObject(departmentUid, Department.class);
		return department;
	}

	@Override
	public <T extends StandardPersistentObject> List<T> getAll(Class<T> clazz) {
		return daoService.getAll(clazz);
	}

	@Override
	public <T extends StandardPersistentObject> void saveAll(List<T> objects) throws Exception {
		daoService.saveAll(objects);
	}

	@Override
	public Doctor findDoctorFromSettings(Days dayInWeek, Integer weekInMonth, TypeOfWork typeOfWork) {
		Parameter parameterDayInWeek = new Parameter("dayInWeek", dayInWeek);
		Parameter parameterWeekInMonth = new Parameter("weekInMonth", weekInMonth);
		Parameter parameterTypeOfWork = new Parameter("typeOfWork", typeOfWork);

		List<Setting> settings = daoService.findObjectsByCriteria(Setting.class, parameterDayInWeek, parameterWeekInMonth, parameterTypeOfWork);
		if (settings == null || settings.isEmpty()) {
			return null;
		}
		Long doctorId = settings.get(0).getDoctorId();
		return daoService.getObject(Doctor.class, doctorId);
	}

	@Override
	public Setting findSetting(Days dayInWeek, Long doctorId, TypeOfWork typeOfWork, Integer weekInMonth) {
		Parameter parameterDayInWeek = new Parameter("dayInWeek", dayInWeek);
		Parameter parameterWeekInMonth = new Parameter("weekInMonth", weekInMonth);
		Parameter parameterTypeOfWork = new Parameter("typeOfWork", typeOfWork);
		Parameter parameterDoctorId = new Parameter("doctorId", doctorId);
		
		List<Setting> settings = daoService.findObjectsByCriteria(Setting.class, parameterDayInWeek, parameterWeekInMonth, parameterTypeOfWork, parameterDoctorId);
		if (settings == null || settings.isEmpty()) {
			return null;
		}
		
		return settings.get(0);
	}

	@Override
	public FreeDay findFreeDayForDoctorOn(Calendar date, Doctor doctor) {
		FreeDay freeDay = daoService.findFreeDayForDoctorOn(date, doctor);
		return freeDay;
	}

	@Override
	public Work findWork(Calendar date, TypeOfWork typeOfWork) {
		Parameter parameterDate = new Parameter("date", date);
		Parameter parameterTypeOfWork = new Parameter("typeOfWork", typeOfWork);
		List<Work> works = daoService.findObjectsByCriteria(Work.class, parameterDate, parameterTypeOfWork);
		if(works==null || works.isEmpty()) {
			return null;
		}
		return works.get(0);
	}

	@Override
	public List<Work> findWorksBetween(Calendar dateFrom, Calendar dateTo) {
		Parameter parameterDateFrom = new Parameter("date", dateFrom, Comparator.GREATER_OR_EQUAL_THEN);
		Parameter parameterDateTo = new Parameter("date", dateTo, Comparator.LOWER_OR_EQUAL_THEN);
		
		List<Work> works = daoService.findObjectsByCriteria(Work.class, parameterDateFrom, parameterDateTo);
		if(works==null || works.isEmpty()) {
			return null;
		}
		return works;
	}

	
	@Override
	public List<Work> getDutiesForYear(Integer year) {
		List<Work> works = daoService.getDutiesForYear(year);
		return works;
	}
	
	@Override
	public List<Work> getDutiesForMonth(Integer year, Integer month) {
		Calendar dateFrom = Calendar.getInstance();
		dateFrom.clear();
		dateFrom.set(year,month,1);
		
		Calendar dateTo = DateUtils.getMaxDate(dateFrom);

		Parameter parameterFromDate = new Parameter("date", dateFrom, Comparator.GREATER_OR_EQUAL_THEN);
		Parameter parameterToDate = new Parameter("date", dateTo, Comparator.LOWER_OR_EQUAL_THEN);
		Parameter parameterTypeOfWork = new Parameter("typeOfWork", TypeOfWork.DUTY);
		
		List<Work> works = daoService.findObjectsByCriteria(Work.class, parameterFromDate, parameterToDate, parameterTypeOfWork);
		return works;
	}

	@Override
	public void addHoliday(Holiday holiday) throws Exception {
		try {
			if (holiday == null) {
				return;
			}
			daoService.saveObject(holiday);
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}
	}

	@Override
	public void deleteHoliday(Holiday holiday) throws Exception {
		daoService.deleteObject(holiday);
	}

	@Override
	public List<Holiday> getAllHolidaysForMonth(Calendar firstDate) {
		Calendar lastDate = DateUtils.getMaxDate(firstDate);
		Parameter parameterFromDate = new Parameter("date", firstDate, Comparator.GREATER_OR_EQUAL_THEN);
		Parameter parameterToDate = new Parameter("date", lastDate, Comparator.LOWER_OR_EQUAL_THEN);

		List<Holiday> holidays = daoService.findObjectsByCriteria(Holiday.class, parameterFromDate, parameterToDate);
		return holidays;
	}
	
	@Override
	public List<Holiday> getHolidaysForYear(Integer year) {
		Calendar firstDate = Calendar.getInstance();
		firstDate.clear();
		firstDate.set(year, 0, 1);
		Calendar lastDate = Calendar.getInstance();
		lastDate.clear();
		lastDate.set(year, 11, 31);
		Parameter parameterFromDate = new Parameter("date", firstDate, Comparator.GREATER_OR_EQUAL_THEN);
		Parameter parameterToDate = new Parameter("date", lastDate, Comparator.LOWER_OR_EQUAL_THEN);

		List<Holiday> holidays = daoService.findObjectsByCriteria(Holiday.class, parameterFromDate, parameterToDate);
		return holidays;
	}

	@Override
	public Doctor getDoctor(Long doctorId) throws Exception {
		Doctor doctor = getObject(doctorId, Doctor.class);
		return doctor;
	}

	@Override
	public void addFreeDay(FreeDay freeDay, Long doctorId) throws Exception {
		Doctor doctor = getDoctor(doctorId);
		ModelUtils.addDependentToParent(freeDay, doctor, Doctor.class, Person.class);
		daoService.updateObject(doctor);
		//addDependentToParent(freeDay, doctorId, Doctor.class, Person.class);
	}

	@Override
	public void deleteFreeDay(FreeDay freeDay) throws Exception {
		daoService.deleteObject(freeDay);
	}
	
	@Override
	public List<FreeDay> getFreeDays(Calendar dateFrom, Calendar dateUntil) throws Exception {
		Parameter parameterDateFrom = new Parameter("dateFrom", dateFrom, Comparator.GREATER_OR_EQUAL_THEN);
		Parameter parameterDateUntil = new Parameter("dateTo", dateUntil , Comparator.LOWER_OR_EQUAL_THEN);
		List<FreeDay> freeDays = daoService.findObjectsByCriteria(FreeDay.class, parameterDateFrom, parameterDateUntil);
		return freeDays;
	}
	
	@Override
	public Work getWorkFor(Calendar date) {
		Parameter parameterDate = new Parameter("date", date);
		Work work = daoService.findObjectByCriteria(Work.class, parameterDate);
		return work;
	}
	
	@SuppressWarnings("rawtypes")
	public <T extends StandardPersistentObject> T getObject(Long id, Class objectClass) throws Exception {
		@SuppressWarnings("unchecked")
		T object = (T) daoService.getObject(objectClass, id);
		return object;
	}

	@Override
	public void addWeekendDutiesInfo(WeekendDutiesInfoCreationPurpose weekendDutiesInfoCreationPurpose, String creationPurpose, Calendar creationDate) throws Exception {
		List<Doctor> doctors = getAll(Doctor.class);
		for(Doctor doctor : doctors) {
			Integer numberOfDuties = 0;
			Integer year = DateUtils.getYearFromDate(creationDate);
			
			WeekendDutiesInfo weekendDutiesInfo = ObjectFactory.create(WeekendDutiesInfo.class);
			weekendDutiesInfo.setCreationDate(creationDate);
			weekendDutiesInfo.setCreationPurpose(creationPurpose);
			weekendDutiesInfo.setYear(year);
			weekendDutiesInfo.setNumberOfDuties(numberOfDuties);
			weekendDutiesInfo.setWeekendDutiesInfoCreationPurpose(weekendDutiesInfoCreationPurpose);
			
			ModelUtils.addDependentToParent(weekendDutiesInfo, doctor, Doctor.class, Person.class);
			daoService.updateObject(doctor);
		}
		
	}

	@Override
	public void updateWeekendDutiesInfo(Doctor doctor) throws Exception {
		WeekendDutiesInfo weekendDutiesInfo = daoService.getWeekendDutiesInfoForMaxCreationDateAndDoctor(doctor);
		//increment dutiesInfoForDoctor
		weekendDutiesInfo.setNumberOfDuties(weekendDutiesInfo.getNumberOfDuties()+1);
		daoService.updateObject(weekendDutiesInfo);
	}

	@Override
	public List<WeekendDutiesInfo> getWeekendDutiesInfosWithMaxCreationDateForDoctors(List<Doctor> doctors) {
		List<WeekendDutiesInfo> weekendDutiesInfos = new ArrayList<WeekendDutiesInfo>();
		for(Doctor doctor : doctors) {
			WeekendDutiesInfo weekendDutiesInfo = daoService.getWeekendDutiesInfoForMaxCreationDateAndDoctor(doctor);
			if(weekendDutiesInfo==null) {
				continue;
			}
			weekendDutiesInfos.add(weekendDutiesInfo);
		}
		return weekendDutiesInfos;
	}
	
	@Override
	public WeekendDutiesInfo getWeekendDutiesInfo(Integer year, WeekendDutiesInfoCreationPurpose weekendDutiesInfoCreationPurpose) {
		Parameter parameterDateFrom = new Parameter("year", year);
		Parameter parameterDateUntil = new Parameter("weekendDutiesInfoCreationPurpose", weekendDutiesInfoCreationPurpose);
		WeekendDutiesInfo weekendDutiesInfo = daoService.findObjectByCriteria(WeekendDutiesInfo.class, parameterDateFrom, parameterDateUntil);
		return weekendDutiesInfo;
	}
}
